package com.ibix.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
import com.google.common.collect.Iterables;
import com.ibix.dao.BaseDao;
import com.ibix.domain.bean.QueryParam;
import com.ibix.domain.entity.BaseEntity;
import com.sun.istack.internal.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.experimental.Delegate;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 项目名称：CaiHai
 * <br>类描述：
 * <br>创建人：htliu
 * <br>创建时间：2017/9/15 10:16
 * <br>修改人:
 * <br>修改时间：2017/9/15 10:16
 * <br>修改备注：
 * <br>@version
 */
@Service
@Data
@Accessors(fluent = true)
@Transactional(rollbackFor = Throwable.class)
public class BaseService {
    protected final Logger LOGGER = LoggerFactory.getLogger(getClass());
    @Resource
    @Delegate
    protected BaseDao baseDao;
    @Resource
    protected RestTemplate restTemplate;
    protected ObjectMapper mapper = new ObjectMapper();
    /**
     * 匹配sql中from 后面sql
     */
    private Pattern fromPattern = Pattern.compile(".*(from.*)", Pattern.CASE_INSENSITIVE);
    /**
     * 匹配order by
     */
    private Pattern orderPattern = Pattern.compile("order by.*", Pattern.CASE_INSENSITIVE);

    @PostConstruct
    public void init() {
//        scheduler = schedulerFactoryBean.getScheduler();
        //设置字符集UTF-8，解决中文乱码
        for (HttpMessageConverter<?> httpMessageConverter : restTemplate.getMessageConverters()) {
            if (httpMessageConverter instanceof StringHttpMessageConverter) {
                StringHttpMessageConverter stringHttpMessageConverter = (StringHttpMessageConverter) httpMessageConverter;
                stringHttpMessageConverter.setDefaultCharset(Charsets.UTF_8);
                break;
            }
        }
    }

    /**
     * 持久化数据到数据库，同时修改时间
     *
     * @param data
     */
    public void addData(BaseEntity data) {
        Date date = new Date();
        data.setCreateTime(date);
        data.setModifyTime(date);
        baseDao.add(data);
    }

    /**
     * 更新数据的修改时间
     *
     * @param data
     */
    public void update(BaseEntity data) {
        data.setModifyTime(new Date());
//        baseDao.update(data);
    }

    /**
     * @param entity
     * @param ignores {@link #wrapIgnores(String...)} 默认包含该参数
     * @return 数据库持久化对象
     */
    protected <T extends BaseEntity> T addOrUpdate(BaseEntity entity, String... ignores) {
        BaseEntity data = baseDao.find(entity.getClass(), entity.getId());
        if (data == null) {
            addData(entity);
            return (T) entity;
        } else {
            BeanUtils.copyProperties(entity, data, wrapIgnores(ignores));
            update(data);
            return (T) data;
        }
    }

    /**
     * 获得notNull实体
     *
     * @param queryParam
     * @param <T>
     * @return
     */
    public @NotNull
    <T extends BaseEntity> T find(QueryParam queryParam) {
        T data = null;
        switch (queryParam.type()) {
            case HQL:
                data = find(queryParam.hql(), queryParam.params().toArray());
                break;
            case PRIMARY_KEY:
                data = find((Class<T>) queryParam.clazz(), queryParam.primaryKey());
                break;
            case SQL:
                data = (T) Iterables.getOnlyElement(listBySql(queryParam.clazz(), queryParam.sql(), queryParam.params().toArray()));
                break;
            default:
        }
        checkData(data, queryParam.errorMessage());
        return data;
    }

    /**
     * @param data
     * @see #checkData(Object, String)
     */
    protected void checkData(Object data) {
        checkData(data, null);
    }

    /**
     * 检查参数是否为空
     *
     * @param data
     * @param messages
     * @throws NullPointerException
     */
    protected void checkData(Object data, String messages) throws NullPointerException {
        if (data == null) {
            throw new NullPointerException(messages);
        }
    }


    protected String[] wrapIgnores(String... params) {
        List<String> list = new ArrayList<>();
        list.add("id");
        list.add("createTime");
        list.add("modifyTime");
        list.addAll(Arrays.asList(params));
        String[] data = new String[list.size()];
        return list.toArray(data);
    }

    public long getCountByHql(String hql, Object... params) {
        hql = convertCountSql(hql);
        List list = listByHql(hql, params);
        return Long.valueOf(Iterables.getOnlyElement(list).toString());
    }

    public long getCountBySql(String sql, Object... params) {
        sql = convertCountSql(sql);
        List list = listByHql(sql, params);
        return Long.valueOf(Iterables.getOnlyElement(list).toString());
    }

    /**
     * 替换sql， select from 之间为count(*)
     *
     * @param sqlOrHql
     * @return
     */
    public String convertCountSql(String sqlOrHql) {
        Matcher fromMatcher = fromPattern.matcher(sqlOrHql);
        if (fromMatcher.matches()) {
            sqlOrHql = "select count(*) " + fromMatcher.group(1);
        }
        sqlOrHql = orderPattern.matcher(sqlOrHql).replaceFirst("");
        return sqlOrHql;
    }

    protected String encode(String content, String salt) {
        Md5Hash md5Hash = new Md5Hash(content, salt);
        return md5Hash.toString();
    }


    /**
     * 检查电话号码合法
     *
     * @param phone
     * @return
     */
    public boolean checkPhoneValid(String phone) {
        if (!StringUtils.isEmpty(phone)) {
            return phone.matches("^1[0-9]{10}$");
        }
        return false;
    }

    /**
     * 获取该表的下一个id
     *
     * @param table
     * @return 该表的最后一条数据主键id+1
     */
    public long generateNextID(String table) {
        String sql = String.format("select id from %s order by id desc limit 1", table);
        return listBySql(sql).stream().limit(1).map(v -> Long.valueOf(v.toString())).reduce(1L, (aLong, aLong2) -> aLong + aLong2);
    }

    /**
     * 返回一天的零点
     *
     * @param date
     * @return
     */
    public Date getTimeAtStartOfDay(Date date) {
        return new DateTime(date).withTimeAtStartOfDay().toDate();
    }

}
